//see: http://www.thegrubbsian.com/?p=100

var Observer = function() {
    this.observations = [];
    this.subscribers  = {};
};
 
var Observation = function(name, func) {
    this.name = name;
    this.func = func;
};
 
Observer.prototype = {
    observe: function(name, func) {
        var exists = this.observations.findAll(function(i) {
            return i.name === name;/* && i.func === func;*/ }).length > 0;
        //console.log(exists)
        if (!exists) { this.observations.push(new Observation(name, func)); }
    },
    
    unobserve: function(name, func) {
        this.observations.remove(function(i) {
            return i.name == name && i.func == func;
        });
    },
    
    fire : function(name, data, scope) {
        scope = (scope||this||window);
        var funcs = this.observations.findAll(function(i) {
            return i.name == name;});
        funcs = funcs.concat(this.subscribers[name]||[]);
        //console.log(funcs);
        funcs.forEach(function(i) { i.func.call(scope, data);});
    },
    
    /*Extended with broadcasting support*/
    subscribe : function(eventName, callback, capture){
        if (!this.subscribers[eventName]) {
            this.subscribers[eventName] = [];}
        this.subscribers[eventName].push(new Observation(eventName, callback));
    },
    
    unsubscribe : function(eventName, callback){
        var subscribers = this.subscribers[eventName]||[];
            subscribers.remove(function(i) {
                //console.log(i.name === eventName && i.func === callback)
                return i.name === eventName && i.func === callback;});
    },
   
    broadcast : function broadcast(eventName, data, scope){
        scope = (scope||this||window);
        var funcs = this.subscribers[eventName]||[];
   	        funcs.forEach(function notify_observer(observer) { 
                observer.func.call(scope, data); });
    },
    
    addEvent : function(eventName, callback, capture){
        return this.subscribe(eventName, callback, capture);
    },
    
    fireEvent : function(eventName, data, scope) {
        return this.broadcast(eventName, data, scope);   
    },
    
    removeEvent : function(eventName, callback){
        return this.unsubscribe(eventName, callback);
    },
    
    addEventListener : function(eventName, callback, capture){
        return this.subscribe(eventName, callback, capture);
    },
    
    dispatchEvent : function(eventName, data, scope) {
        return this.broadcast(eventName, data, scope);   
    },
    
    removeEventListener : function(eventName, callback){
        return this.unsubscribe(eventName, callback);
    }
};
